/* 
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

Title : com.Angaliwa.Shared
Author : Dave Jansen -- Zenitum
URL : http://www.zenitum.com/

Description : The Shared Objects module of Angaliwa. This
			  module allows you to easily use Shared
			  Objects when working with an FMS Server.

History		: v1.1.090713 Re-wrote large parts of the module to
			  match Angaliwa 1.0b's new guidelines. Renamed the
			  module from 'SharedObjects' to avoid confusion
			  with Flex' build-in SharedObject.
			  
			  v1.0.090603 Initial version

Created 	: June 3rd, 2009

Version		: 1.1
Compatibility: 1.1

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
*/

package com.Angaliwa
{
	import flash.events.SyncEvent;
	import flash.net.NetConnection;
	import flash.net.ObjectEncoding;
	import flash.net.SharedObject;
	
	public class Shared
	{
		/* Currently known daisyChain actions:
		 * - syncHandler
		 * - 
		 * - 
		 */
		public var daisyChain:DaisyChain = new DaisyChain(this);
		 
		private var nc:NetConnection;
		public var so:SharedObject;
		private var name:String;
		
		// All data is stored in these two variables, where
		// raw contains all the event information, not just
		// the event data (event.target.data).
		public var data:Object = [];
		public var raw:Object = [];
		
		// For when comparing new information with what
		// we had before, we'll always store all data
		// up to one revision back.
		private var prevData:Object = [];
		private var prevRaw:Object = []
		
		/*
		 * Summary:      	Initializes the Shared Object
		 * Return:       	Void
		 * Version:   	 	1.0
		 * Compatibility:	1.0
		 */
		public function Shared(netConnection:*, sharedObjectName:String, persistentConnection:Boolean=false){
			// To not make us re-define this thing over and over, we'll
			// save the netconnection in this class instance.
			if(netConnection is NetConnection)
				nc = netConnection;
			else if(netConnection is FMSConnect)
				nc = netConnection.g();
			
			name = sharedObjectName;
			
			// Set the proper encoding.
			SharedObject.defaultObjectEncoding = flash.net.ObjectEncoding.AMF0;
			
			// Configure and connect the remote shared object.
			so = SharedObject.getRemote(sharedObjectName, nc.uri, persistentConnection);
			so.addEventListener(SyncEvent.SYNC, syncHandler);
			so.connect(nc);
		}
		
		/*
		 * Summary:      	Handles all sync events.
		 * Return:       	Void
		 * Version:   	 	1.0
		 * Compatibility:	1.0
		 */
		public function syncHandler(event:SyncEvent):void {
			// Store the data in more easily accessible variables.
			data = event.target.data;
			raw  = event;
			
			daisyChain.run('syncHandler', event);
		}
		
		/*
		 * Summary:      	Checks if the requested variable exists, and
		 					if it does, returns it. Returns false when
		 					the variable does not exist.
		 * Return:       	*
		 * Version:   	 	1.0
		 * Compatibility:	1.0
		 */
		public function g(n:String):*{
			var t:Array = n.split(".");
			var crnt:* = data;
			
			for(var i:int=0; i < t.length; i++){
				var tmp:String = t[i];
				if(typeof crnt[tmp] != undefined){
					crnt = crnt[tmp];
				}
				else{
					// TODO: Add debug integration later on.
					trace('[SHARED] [G] It seems like '+n+' does not exist, got stuck at '+tmp+'.');
					return false;
				}
			}
			
			return crnt;
		}
		
		/*
		 * Summary:      	Either adds a new variable/object to the 
		 					variableHolder, or updates an already-
		 					existing one. Returns the added/updated
		 					object, which allows for code-chaining.
		 					Also triggers the SharedObject send()
		 					function, so all connected clients will
		 					receive the changes that were made.
		 * Return:       	*
		 * Version:   	 	1.0
		 * Compatibility:	1.0
		 */
		public function a(n:String, v:*):*{
			// For some reason when working with arrays
			// we need to clear the array before we can
			// insert the newly updated one. Without this
			// the server won't recognize it updated, and
			// not push the new version to all connected
			// clients.
			if(this.so.data[n])
				this.so.setProperty(n, null);
			
			// Set the new property of the specified
			// variable.
			this.so.setProperty(n, v);
			
			// Return the object, for chaining support.
			return this;
			
			/*
			Typical usage of SharedObjects:
				
				settings > general > title
									 sessionID
									 somethingElse
						   someRandomVariable
				
				clients > clientID > clientID
									 clientName
									 clientEmail
									 clientColor
									 clientWhiteboardId
									 clientWhatever
									 clientEtc
						  clientID > clientID
									 clientName
									 clientEmail
									 clientColor
									 clientWhiteboardId
									 clientWhatever
									 clientEtc
				
				something > someVariable
				
				textChat > entry[x] > clientID
									  dateTime
									  body
				
				history > entry[x] > history message
				
				
			
				So, how would one add/edit settings..
				
				Shared.a('settings.general.title', 'Something');
				Shared.a('clients.clientID', [object]);
				Shared.a('something', 'someVariable');
				
				
				Ways to receive data..
				
				Shared.g('clients').length();
				Shared.g('clients.clientID'); // returns [object] with client data.
			*/
		}
		
		/*
		 * Summary:      	Removes (deletes) a variable/object from
		 					the variableHolder, if it exists. Returns
		 					true on successful deletion, or false when
		 					the variable did not exist to begin with.
		 * Return:       	Boolean
		 * Version:   	 	1.0
		 * Compatibility:	1.0
		 */
		public function d(n:String):Boolean{
			var t:Array = n.split(".");
			var crnt:* = data;
			
			for(var i:int=0; i < t.length; i++){
				var tmp:String = t[i];
				if(crnt.hasOwnProperty(tmp)){
					if(i < t.length-1){
						crnt = crnt[tmp];
					}
					else{
					  	// Trigger the SharedObject update.
						this.so.setProperty(n, undefined);
						//this.so.send();
					  	
					  	return true;
					}
				}
			}
			
			return false;
		}
	}
}